/* Copyright (C) 2010-2011, Mamadou Diop.
 *  Copyright (C) 2011, Doubango Telecom.
 *
 * Contact: Mamadou Diop <diopmamadou(at)doubango(dot)org>
 *	
 * This file is part of imsdroid Project (http://code.google.com/p/imsdroid)
 *
 * imsdroid is free software: you can redistribute it and/or modify it under the terms of 
 * the GNU General Public License as published by the Free Software Foundation, either version 3 
 * of the License, or (at your option) any later version.
 *	
 * imsdroid is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * See the GNU General Public License for more details.
 *	
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, write to the Free Software Foundation, Inc., 
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package org.doubango.ngn.services.impl;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.util.UUID;

import android.content.BroadcastReceiver;

import org.doubango.aspect.impl.NgnSipServiceImpl;
import org.doubango.ngn.NgnApplication;
import org.doubango.ngn.NgnEngine;
import org.doubango.ngn.events.NgnInviteEventArgs;
import org.doubango.ngn.events.NgnInviteEventTypes;
import org.doubango.ngn.events.NgnMessagingEventArgs;
import org.doubango.ngn.events.NgnMessagingEventTypes;
import org.doubango.ngn.events.NgnNetworkEventArgs;
import org.doubango.ngn.events.NgnNetworkEventTypes;
import org.doubango.ngn.events.NgnPublicationEventArgs;
import org.doubango.ngn.events.NgnPublicationEventTypes;
import org.doubango.ngn.events.NgnRegistrationEventArgs;
import org.doubango.ngn.events.NgnRegistrationEventTypes;
import org.doubango.ngn.events.NgnSubscriptionEventArgs;
import org.doubango.ngn.events.NgnSubscriptionEventTypes;
import org.doubango.ngn.model.NgnDeviceInfo.Orientation;
import org.doubango.ngn.services.INgnConfigurationService;
import org.doubango.ngn.services.INgnNetworkService;
import org.doubango.ngn.services.INgnSipService;
import org.doubango.ngn.sip.NgnAVSession;
import org.doubango.ngn.sip.NgnInviteSession;
import org.doubango.ngn.sip.NgnMessagingSession;
import org.doubango.ngn.sip.NgnMsrpSession;
import org.doubango.ngn.sip.NgnPresenceStatus;
import org.doubango.ngn.sip.NgnPublicationSession;
import org.doubango.ngn.sip.NgnRegistrationSession;
import org.doubango.ngn.sip.NgnSipSession;
import org.doubango.ngn.sip.NgnSipStack;
import org.doubango.ngn.sip.NgnSubscriptionSession;
import org.doubango.ngn.sip.NgnInviteSession.InviteState;
import org.doubango.ngn.sip.NgnSipSession.ConnectionState;
import org.doubango.ngn.sip.NgnSipStack.STACK_STATE;
import org.doubango.ngn.sip.NgnSubscriptionSession.EventPackageType;
import org.doubango.ngn.utils.NgnConfigurationEntry;
import org.doubango.ngn.utils.NgnContentType;
import org.doubango.ngn.utils.NgnDateTimeUtils;
import org.doubango.ngn.utils.NgnNetworkConnection;
import org.doubango.ngn.utils.NgnStringUtils;
import org.doubango.ngn.utils.NgnUriUtils;
import org.doubango.tinyWRAP.CallSession;
import org.doubango.tinyWRAP.DDebugCallback;
import org.doubango.tinyWRAP.DialogEvent;
import org.doubango.tinyWRAP.InviteEvent;
import org.doubango.tinyWRAP.InviteSession;
import org.doubango.tinyWRAP.MessagingEvent;
import org.doubango.tinyWRAP.MessagingSession;
import org.doubango.tinyWRAP.MsrpSession;
import org.doubango.tinyWRAP.OptionsEvent;
import org.doubango.tinyWRAP.OptionsSession;
import org.doubango.tinyWRAP.RPMessage;
import org.doubango.tinyWRAP.SMSData;
import org.doubango.tinyWRAP.SMSEncoder;
import org.doubango.tinyWRAP.SipCallback;
import org.doubango.tinyWRAP.SipMessage;
import org.doubango.tinyWRAP.SipSession;
import org.doubango.tinyWRAP.SipStack;
import org.doubango.tinyWRAP.StackEvent;
import org.doubango.tinyWRAP.SubscriptionEvent;
import org.doubango.tinyWRAP.SubscriptionSession;
import org.doubango.tinyWRAP.tinyWRAPConstants;
import org.doubango.tinyWRAP.tsip_invite_event_type_t;
import org.doubango.tinyWRAP.tsip_message_event_type_t;
import org.doubango.tinyWRAP.tsip_options_event_type_t;
import org.doubango.tinyWRAP.tsip_request_type_t;
import org.doubango.tinyWRAP.tsip_subscribe_event_type_t;
import org.doubango.tinyWRAP.twrap_media_type_t;
import org.doubango.tinyWRAP.twrap_sms_type_t;
import org.doubango.utils.Util;

import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.ConditionVariable;
import android.os.Looper;
import android.util.Base64;
import android.util.Log;
import android.widget.Toast;

import com.evendai.loglibrary.Timber;

public class NgnSipService extends NgnBaseService implements INgnSipService,
        tinyWRAPConstants {
    private final static String TAG = NgnSipService.class.getCanonicalName();

    private NgnRegistrationSession mRegSession;
    private NgnSipStack mSipStack;
    private final DDebugCallback mDebugCallback;
    private final MySipCallback mSipCallback;

    private final INgnConfigurationService mConfigurationService;
    private final INgnNetworkService mNetworkService;

    private NgnNetworkConnection mActiveConnection;

    private ConditionVariable mCondHackAoR;

    private BroadcastReceiver mBroadCastRecv;

    private boolean mTerminateRequested;
    public NgnSipService() {
        super();

        mDebugCallback = new DDebugCallback();
        mSipCallback = new MySipCallback(this);

        mConfigurationService = NgnEngine.getInstance().getConfigurationService();
        mNetworkService = NgnEngine.getInstance().getNetworkService();
    }

    @Override
    public boolean start() {
//        Log.d(TAG, "starting...");
        if (mBroadCastRecv == null) {
            mBroadCastRecv = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    if (NgnNetworkEventArgs.ACTION_NETWORK_EVENT.equals(intent.getAction())) {
                        handleNetworkEvent(intent);
                    }
                }
            };
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(NgnNetworkEventArgs.ACTION_NETWORK_EVENT);
            NgnApplication.getContext().registerReceiver(mBroadCastRecv, intentFilter);
        }
        return true;
    }

    @Override
    public boolean stop() {
//        Log.d(TAG, "stopping...");
        mTerminateRequested = true;
        boolean retBool = true;
        if (mSipStack != null && mSipStack.isValid()) {
            if (mSipStack.getState() == STACK_STATE.STARTED) {
                retBool = mSipStack.stop();
            }
        }
        if (mBroadCastRecv != null) {
            NgnApplication.getContext().unregisterReceiver(mBroadCastRecv);
            mBroadCastRecv = null;
        }
        return retBool;
    }

    @Override
    public String getDefaultIdentity() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setDefaultIdentity(String identity) {
        // TODO Auto-generated method stub

    }

    @Override
    public NgnSipStack getSipStack() {
        return mSipStack;
    }

    @Override
    public NgnNetworkConnection getActiveConnection() {
        return mActiveConnection;
    }

    @Override
    public boolean isRegistered() {
        if (mRegSession != null) {
            return mRegSession.isConnected();
        }
        return false;
    }

    @Override
    public ConnectionState getRegistrationState() {
        if (mRegSession != null) {
            return mRegSession.getConnectionState();
        }
        return ConnectionState.NONE;
    }

    @Override
    public boolean isXcapEnabled() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isPublicationEnabled() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isSubscriptionEnabled() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isSubscriptionToRLSEnabled() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public int getCodecs() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public void setCodecs(int coddecs) {
        // TODO Auto-generated method stub

    }

    @Override
    public byte[] getSubRLSContent() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public byte[] getSubRegContent() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public byte[] getSubMwiContent() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public byte[] getSubWinfoContent() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean stopStack() {
        if (mSipStack != null) {
            mSipStack.stop();
        }
        return false;
    }

    @Override
    public boolean register(Context context) {
//        Log.d(TAG, "register()");

        mTerminateRequested = false;

        final String realm = mConfigurationService.getString(NgnConfigurationEntry.NETWORK_REALM, NgnConfigurationEntry.DEFAULT_NETWORK_REALM); // doubango.org
        final String impi = mConfigurationService.getString(NgnConfigurationEntry.IDENTITY_IMPI, NgnConfigurationEntry.DEFAULT_IDENTITY_IMPI);  // sip:20010@doubango.org
        final String impu = mConfigurationService.getString(NgnConfigurationEntry.IDENTITY_IMPU, NgnConfigurationEntry.DEFAULT_IDENTITY_IMPU);  // 20010
//        Log.d(TAG, String.format("realm='%s', impu='%s', impi='%s'", realm, impu, impi));

        if (mSipStack == null) {
            mSipStack = new NgnSipStack(mSipCallback, realm, impi, impu);
            mSipStack.setDebugCallback(mDebugCallback);
            SipStack.setCodecs_2(mConfigurationService.getInt(NgnConfigurationEntry.MEDIA_CODECS,NgnConfigurationEntry.DEFAULT_MEDIA_CODECS));
        } else {
            if (!mSipStack.setRealm(realm)) {
                Log.e(TAG, "Failed to set realm");
                return false;
            }
            if (!mSipStack.setIMPI(impi)) {
                Log.e(TAG, "Failed to set IMPI");
                return false;
            }
            if (!mSipStack.setIMPU(impu)) {
                Log.e(TAG, "Failed to set IMPU");
                return false;
            }
        }

//        try { displayName = Base64.encodeToString(displayName.getBytes("UTF-8"), Base64.NO_WRAP); } catch (UnsupportedEncodingException e) { e.printStackTrace(); }
        String displayName = mConfigurationService.getString(
                NgnConfigurationEntry.IDENTITY_DISPLAY_NAME,
                NgnConfigurationEntry.DEFAULT_IDENTITY_DISPLAY_NAME
        );
        Timber.Companion.i("setDisplayName = " + displayName);
        mSipStack.setDisplayName(displayName);

        // set the Password
        mSipStack.setPassword(mConfigurationService.getString(NgnConfigurationEntry.IDENTITY_PASSWORD, NgnConfigurationEntry.DEFAULT_IDENTITY_PASSWORD));
        // Set AMF
        mSipStack.setAMF(mConfigurationService.getString(NgnConfigurationEntry.SECURITY_IMSAKA_AMF, NgnConfigurationEntry.DEFAULT_SECURITY_IMSAKA_AMF));
        // Set Operator Id
        mSipStack.setOperatorId(mConfigurationService.getString(NgnConfigurationEntry.SECURITY_IMSAKA_OPID, NgnConfigurationEntry.DEFAULT_SECURITY_IMSAKA_OPID));

        // Check stack validity
        if (!mSipStack.isValid()) {
            Log.e(TAG, "Trying to use invalid stack");
            return false;
        }

        // Set STUN information
        mSipStack.setSTUNEnabled(mConfigurationService.getBoolean(NgnConfigurationEntry.NATT_USE_STUN_FOR_SIP, NgnConfigurationEntry.DEFAULT_NATT_USE_STUN_FOR_SIP));
        if (mConfigurationService.getBoolean(NgnConfigurationEntry.NATT_STUN_DISCO, NgnConfigurationEntry.DEFAULT_NATT_STUN_DISCO)) {
            String domain = realm.substring(realm.indexOf(':') + 1);
            int[] port = new int[1];
            String server = mSipStack.dnsSrv(String.format("_stun._udp.%s", domain), port);
            if (server == null) {
                Log.e(TAG, "STUN discovery has failed");
            }
//            Log.d(TAG, String.format("STUN1 - server=%s and port=%d",
//                    server, port[0]));
            mSipStack.setSTUNServer(server, port[0]);// Needed event if null
        } else {
            final String server = mConfigurationService.getString(NgnConfigurationEntry.NATT_STUN_SERVER, NgnConfigurationEntry.DEFAULT_NATT_STUN_SERVER);
            final int port = mConfigurationService.getInt(NgnConfigurationEntry.NATT_STUN_PORT, NgnConfigurationEntry.DEFAULT_NATT_STUN_PORT);
//            Log.d(NgnSipService.TAG, String.format("STUN2 - server=%s and port=%d", server, port));
            mSipStack.setSTUNServer(server, port);
        }

        // Set Proxy-CSCF
        final String pcscf_host = mConfigurationService.getString(NgnConfigurationEntry.NETWORK_PCSCF_HOST, null); // null will trigger DNS NAPTR+SRV
        final int pcscf_port = mConfigurationService.getInt(NgnConfigurationEntry.NETWORK_PCSCF_PORT, NgnConfigurationEntry.DEFAULT_NETWORK_PCSCF_PORT);
        final String transport = mConfigurationService.getString(NgnConfigurationEntry.NETWORK_TRANSPORT, NgnConfigurationEntry.DEFAULT_NETWORK_TRANSPORT);
        final String ipversion = mConfigurationService.getString(NgnConfigurationEntry.NETWORK_IP_VERSION, NgnConfigurationEntry.DEFAULT_NETWORK_IP_VERSION); // "ipv6", "ipv4", "ipv46", "ipv64"

        Log.d(TAG, String.format("pcscf-host='%s', pcscf-port='%d', transport='%s', ipversion='%s'", pcscf_host, pcscf_port, transport, ipversion));

        // Set Proxy-CSCF
        if (!mNetworkService.setProxyCSCF(transport, ipversion, pcscf_host, pcscf_port)) {
            Log.e(TAG, "Failed to set Proxy-CSCF parameters on network service");
            return false;
        }
        if (!mSipStack.setProxyCSCF(pcscf_host, pcscf_port, transport, ipversion)) {
            Log.e(TAG, "Failed to set Proxy-CSCF parameters on SIP stack");
            return false;
        }

        // TODO 这里的ipversion变量是我们在设置中的network界面中是选择了IPv4还是选择了IPv6，但是设置这个根本没有用，没法指定使不使用ipv6
        // Set default connection
        final boolean ipv6 = NgnStringUtils.equals(ipversion, "ipv6", true); // "ipv46" and "ipv64" use "IPv4" as default transport
        final NgnNetworkConnection defaultConnection = mNetworkService.getBestConnection(ipv6);
        // TODO 如果这个连接是一个IPV6地址，则无法使用，看localIP和useIPv6属性可知道是否是IPv6地址，不要看它的mIPversion属性，有很多地方设置的，一会设置为ipv6一会又改成了ipv4，根本不准的
        Log.e(TAG, "使用的网络连接为：" + defaultConnection);
        if (defaultConnection == null) {
            Log.e(TAG, "Failed to find default connection");
            return false;
        }
//        Log.d(TAG, "SIP service using default connection ->" + defaultConnection);
        // Bind process to connection
        //mNetworkService.bindProcessToConnection(defaultConnection);
        Log.e(TAG, "设置的本地ip为：" + defaultConnection.getLocalIP());
        // Set local IP
        if (!mSipStack.setLocalIP(defaultConnection.getLocalIP())) {
            Log.e(TAG, "Failed to set the local IP");
            return false;
        }

        // Whether to use DNS NAPTR+SRV for the Proxy-CSCF discovery (even if
        // the DNS requests are sent only when the stack starts,
        // should be done after setProxyCSCF())
        String discoverType = mConfigurationService.getString(NgnConfigurationEntry.NETWORK_PCSCF_DISCOVERY, NgnConfigurationEntry.DEFAULT_NETWORK_PCSCF_DISCOVERY);
        mSipStack.setDnsDiscovery(NgnStringUtils.equals(discoverType, NgnConfigurationEntry.PCSCF_DISCOVERY_DNS_SRV, true));

        // enable/disable 3GPP early IMS
        mSipStack.setEarlyIMS(mConfigurationService.getBoolean(NgnConfigurationEntry.NETWORK_USE_EARLY_IMS, NgnConfigurationEntry.DEFAULT_NETWORK_USE_EARLY_IMS));

        // SigComp (only update compartment Id if changed)
        if (mConfigurationService.getBoolean(NgnConfigurationEntry.NETWORK_USE_SIGCOMP, NgnConfigurationEntry.DEFAULT_NETWORK_USE_SIGCOMP)) {
            String compId = String.format("urn:uuid:%s", UUID.randomUUID().toString());
            mSipStack.setSigCompId(compId);
        } else {
            mSipStack.setSigCompId(null);
        }

        // TLS
        final String pvFilePath = mConfigurationService.getString(NgnConfigurationEntry.SECURITY_TLS_PRIVKEY_FILE_PATH, NgnConfigurationEntry.DEFAULT_SECURITY_TLS_PRIVKEY_FILE_PATH);
        final String pbFilePath = mConfigurationService.getString(NgnConfigurationEntry.SECURITY_TLS_PUBKEY_FILE_PATH, NgnConfigurationEntry.DEFAULT_SECURITY_TLS_PUBKEY_FILE_PATH);
        final String caFilePath = mConfigurationService.getString(NgnConfigurationEntry.SECURITY_TLS_CA_FILE_PATH, NgnConfigurationEntry.DEFAULT_SECURITY_TLS_CA_FILE_PATH);
        final boolean verifyCerts = mConfigurationService.getBoolean(NgnConfigurationEntry.SECURITY_TLS_VERIFY_CERTS, NgnConfigurationEntry.DEFAULT_SECURITY_TLS_VERIFY_CERTS);
//        Log.d(TAG, String.format("TLS - pvk='%s' pbk='%s' ca='%s' verify=%s",
//                pvFilePath, pbFilePath, caFilePath, verifyCerts));
        if (!mSipStack.setSSLCertificates(pvFilePath, pbFilePath, caFilePath, verifyCerts)) {
            Log.e(TAG, "Failed to set TLS certificates");
            return false;
        }

        // Start the Stack
        if (!mSipStack.start()) {
            if (context != null && Thread.currentThread() == Looper.getMainLooper().getThread()) {
                Toast.makeText(context, "Failed to start the SIP stack", Toast.LENGTH_LONG).show();
            }
            Log.e(TAG, "Failed to start the SIP stack");
            return false;
        }

        // Update active connection
        mActiveConnection = defaultConnection;

        // Preference values
        final boolean xcapEnabled = mConfigurationService.getBoolean(NgnConfigurationEntry.XCAP_ENABLED, NgnConfigurationEntry.DEFAULT_XCAP_ENABLED);
        final boolean presenceEnabled = mConfigurationService.getBoolean(NgnConfigurationEntry.RCS_USE_PRESENCE, NgnConfigurationEntry.DEFAULT_RCS_USE_PRESENCE);
        final boolean mwiEnabled = mConfigurationService.getBoolean(NgnConfigurationEntry.RCS_USE_MWI, NgnConfigurationEntry.DEFAULT_RCS_USE_MWI);

        // Create registration session
        if (mRegSession == null) {
            mRegSession = new NgnRegistrationSession(mSipStack);
        } else {
            mRegSession.setSigCompId(mSipStack.getSigCompId());
        }
        mRegSession.setExpires(60);
        // Set/update From URI. For Registration ToUri should be equals to realm
        // (done by the stack)
        mRegSession.setFromUri(impu);

		/* Before registering, check if AoR hacking id enabled */
        final boolean hackAoR = mConfigurationService.getBoolean(NgnConfigurationEntry.NATT_HACK_AOR, NgnConfigurationEntry.DEFAULT_NATT_HACK_AOR);
        if (hackAoR) {
            if (mCondHackAoR == null) {
                mCondHackAoR = new ConditionVariable();
            }
            final OptionsSession optSession = new OptionsSession(mSipStack);
            // optSession.setToUri(String.format("sip:%s@%s", "hacking_the_aor",
            // this.preferences.realm));
            optSession.send();
            try {
                synchronized (mCondHackAoR) {
                    mCondHackAoR.wait(mConfigurationService.getInt(NgnConfigurationEntry.NATT_HACK_AOR_TIMEOUT, NgnConfigurationEntry.DEFAULT_NATT_HACK_AOR_TIMEOUT));
                }
            } catch (InterruptedException e) {
                Log.e(TAG, e.getMessage());
            }
            mCondHackAoR = null;
            optSession.delete();
        }

        if (!mRegSession.register()) {
            Log.e(TAG, "Failed to send REGISTER request");
            return false;
        }

        return true;
    }

    @Override
    public boolean unRegister() {
        Log.d(TAG, "unRegister()");
        if (isRegistered()) {
            mTerminateRequested = true;
            new Thread(() -> mSipStack.stop()).start();
        }
        return true;
    }

    @Override
    public boolean PresencePublish() {
        return false;
    }

    @Override
    public boolean PresencePublish(NgnPresenceStatus status) {
        // TODO Auto-generated method stub
        return false;
    }

    private void broadcastRegistrationEvent(NgnRegistrationEventArgs args) {
        final Intent intent = new Intent(NgnRegistrationEventArgs.ACTION_REGISTRATION_EVENT);
        intent.putExtra(NgnRegistrationEventArgs.EXTRA_EMBEDDED, args);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void broadcastInviteEvent(NgnInviteEventArgs args, short sipCode) {
        if (sipCode == 901) {
            synchronized (NgnSipServiceImpl.sessionArray) {
                NgnSipServiceImpl.sessionArray.put((int) args.getSessionId(), true);
                NgnSipServiceImpl.sessionArray.notifyAll();
            }
        }
        final Intent intent = new Intent(NgnInviteEventArgs.ACTION_INVITE_EVENT);
        intent.putExtra(NgnInviteEventArgs.EXTRA_EMBEDDED, args);
        intent.putExtra(NgnInviteEventArgs.EXTRA_SIPCODE, sipCode);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void broadcastTransferRequestEvent(NgnInviteEventArgs args, String referToUri) {
//		synchronized (NgnSipServiceImpl.sessionArray) {
//			NgnSipServiceImpl.sessionArray.put((int) args.getSessionId(), true);
//			NgnSipServiceImpl.sessionArray.notifyAll();
//		}
        final Intent intent = new Intent(NgnInviteEventArgs.ACTION_INVITE_EVENT);
        intent.putExtra(NgnInviteEventArgs.EXTRA_EMBEDDED, args);
        intent.putExtra(NgnInviteEventArgs.EXTRA_REFERTO_URI, referToUri);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void broadcastInviteEvent(NgnInviteEventArgs args) {
        final Intent intent = new Intent(NgnInviteEventArgs.ACTION_INVITE_EVENT);
        intent.putExtra(NgnInviteEventArgs.EXTRA_EMBEDDED, args);
        intent.putExtra(NgnInviteEventArgs.EXTRA_SIPCODE, 0);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void broadcastMessagingEvent(NgnMessagingEventArgs args, String remoteParty, String date) {
        final Intent intent = new Intent(NgnMessagingEventArgs.ACTION_MESSAGING_EVENT);
        intent.putExtra(NgnMessagingEventArgs.EXTRA_REMOTE_PARTY, remoteParty);
        intent.putExtra(NgnMessagingEventArgs.EXTRA_DATE, date);
        intent.putExtra(NgnMessagingEventArgs.EXTRA_EMBEDDED, args);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void broadcastPublicationEvent(NgnPublicationEventArgs args) {
        final Intent intent = new Intent(NgnPublicationEventArgs.ACTION_PUBLICATION_EVENT);
        intent.putExtra(NgnPublicationEventArgs.EXTRA_EMBEDDED, args);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void broadcastSubscriptionEvent(NgnSubscriptionEventArgs args) {
        final Intent intent = new Intent(NgnSubscriptionEventArgs.ACTION_SUBSCRIBTION_EVENT);
        intent.putExtra(NgnSubscriptionEventArgs.EXTRA_EMBEDDED, args);
        Util.INSTANCE.sendBroadcastAsUser(intent);
    }

    private void handleNetworkEvent(Intent intent) {
        final NgnNetworkEventArgs args = intent.getParcelableExtra(NgnNetworkEventArgs.EXTRA_EMBEDDED);
        if (args == null) {
            Log.e(TAG, "Invalid event args");
            return;
        }
        final NgnNetworkEventTypes eventType = args.getEventType();
        switch (eventType) {
            case DISCONNECTED:
                break;
            case CONNECTED:
                //if (!mTerminateRequested && !isRegistered()) {
                //final NgnNetworkConnection bestConnection = mNetworkService.getBestConnection(
                //		NgnStringUtils.equals(mConfigurationService.getString(
                //				NgnConfigurationEntry.NETWORK_IP_VERSION,
                //				NgnConfigurationEntry.DEFAULT_NETWORK_IP_VERSION), "ipv6", true));
                //if (bestConnection != null && bestConnection.isUp()) {
                //register();
                //}
                //}
                break;
        }
    }

    /**
     * MySipCallback
     */
    static class MySipCallback extends SipCallback {
        private final NgnSipService mSipService;

        private MySipCallback(NgnSipService sipService) {
            super();

            mSipService = sipService;
        }

        @Override
        public int OnDialogEvent(DialogEvent e) {
            final String phrase = e.getPhrase();
            final short eventCode = e.getCode();
            final short sipCode;
            final SipSession session = e.getBaseSession();

            if (session == null) {
                return 0;
            }

            final long sessionId = session.getId();
            final SipMessage message = e.getSipMessage();
            NgnSipSession mySession = null;

            sipCode = (message != null && message.isResponse()) ? message.getResponseCode() : eventCode;

//            Log.d(TAG, String.format("OnDialogEvent (%s,%d)", phrase, sessionId));

            switch (eventCode) {
                // == Connecting ==
                case tinyWRAPConstants.tsip_event_code_dialog_connecting: {
                    // Registration
                    if (mSipService.mRegSession != null && mSipService.mRegSession.getId() == sessionId) {
                        mSipService.mRegSession.setConnectionState(ConnectionState.CONNECTING);
                        mSipService.broadcastRegistrationEvent(new NgnRegistrationEventArgs(sessionId, NgnRegistrationEventTypes.REGISTRATION_INPROGRESS, eventCode, phrase));
                    }
                    // Audio/Video/MSRP(Chat, FileTransfer)
                    else if (((mySession = NgnAVSession.getSession(sessionId)) != null) || ((mySession = NgnMsrpSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.CONNECTING);
                        ((NgnInviteSession) mySession).setState(InviteState.INPROGRESS);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(sessionId, NgnInviteEventTypes.INPROGRESS, ((NgnInviteSession) mySession).getMediaType(), phrase), sipCode);
                    }
                    // Publication
                    else if (((mySession = NgnPublicationSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.CONNECTING);
                        mSipService.broadcastPublicationEvent(new NgnPublicationEventArgs(sessionId, NgnPublicationEventTypes.PUBLICATION_INPROGRESS, eventCode, phrase));
                    }
                    // Subscription
                    else if (((mySession = NgnSubscriptionSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.CONNECTING);
                        mSipService.broadcastSubscriptionEvent(new NgnSubscriptionEventArgs(
                                        sessionId,
                                        NgnSubscriptionEventTypes.SUBSCRIPTION_INPROGRESS,
                                        eventCode, phrase, null, null,
                                        ((NgnSubscriptionSession) mySession).getEventPackage()));
                    }

                    break;
                }

                // == Connected == //
                case tinyWRAPConstants.tsip_event_code_dialog_connected: {
                    // Registration
                    if (mSipService.mRegSession != null && mSipService.mRegSession.getId() == sessionId) {
                        mSipService.mRegSession.setConnectionState(ConnectionState.CONNECTED);
                        // Update default identity (vs barred)
                        String _defaultIdentity = mSipService.mSipStack.getPreferredIdentity();
                        if (!NgnStringUtils.isNullOrEmpty(_defaultIdentity)) {
                            mSipService.setDefaultIdentity(_defaultIdentity);
                        }
                        mSipService.broadcastRegistrationEvent(new NgnRegistrationEventArgs(sessionId, NgnRegistrationEventTypes.REGISTRATION_OK, sipCode, phrase));
                    }
                    // Audio/Video/MSRP(Chat, FileTransfer)
                    else if (((mySession = NgnAVSession.getSession(sessionId)) != null) || ((mySession = NgnMsrpSession.getSession(sessionId)) != null)) {
//					synchronized (NgnSipServiceImpl.sessionArray) {
//						NgnSipServiceImpl.sessionArray.put((int) sessionId, true);
//						NgnSipServiceImpl.sessionArray.notifyAll();
//					}
                        mySession.setConnectionState(ConnectionState.CONNECTED);
                        ((NgnInviteSession) mySession).setState(InviteState.INCALL);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(sessionId, NgnInviteEventTypes.CONNECTED, ((NgnInviteSession) mySession).getMediaType(), phrase), sipCode);
                    }
                    // Publication
                    else if (((mySession = NgnPublicationSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.CONNECTED);
                        mSipService.broadcastPublicationEvent(new NgnPublicationEventArgs(sessionId, NgnPublicationEventTypes.PUBLICATION_OK, sipCode, phrase));
                    }
                    // Subscription
                    else if (((mySession = NgnSubscriptionSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.CONNECTED);
                        mSipService.broadcastSubscriptionEvent(new NgnSubscriptionEventArgs(
                                        sessionId,
                                        NgnSubscriptionEventTypes.SUBSCRIPTION_OK,
                                        sipCode, phrase, null, null,
                                        ((NgnSubscriptionSession) mySession).getEventPackage()));
                    }

                    break;
                }

                // == Terminating == //
                case tinyWRAPConstants.tsip_event_code_dialog_terminating: {
                    // Registration
                    if (mSipService.mRegSession != null && mSipService.mRegSession.getId() == sessionId) {
                        mSipService.mRegSession.setConnectionState(ConnectionState.TERMINATING);
                        mSipService.broadcastRegistrationEvent(new NgnRegistrationEventArgs(sessionId, NgnRegistrationEventTypes.UNREGISTRATION_INPROGRESS, eventCode, phrase));
                    }
                    // Audio/Video/MSRP(Chat, FileTransfer)
                    else if (((mySession = NgnAVSession.getSession(sessionId)) != null) || ((mySession = NgnMsrpSession.getSession(sessionId)) != null)) {
//					NgnSipServiceImpl.sessionArray.remove((int) sessionId);
                        mySession.setConnectionState(ConnectionState.TERMINATING);
                        ((NgnInviteSession) mySession).setState(InviteState.TERMINATING);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(sessionId, NgnInviteEventTypes.TERMWAIT, ((NgnInviteSession) mySession).getMediaType(), phrase), sipCode);
                    }
                    // Publication
                    else if (((mySession = NgnPublicationSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.TERMINATING);
                        mSipService.broadcastPublicationEvent(new NgnPublicationEventArgs(sessionId, NgnPublicationEventTypes.UNPUBLICATION_INPROGRESS, eventCode, phrase));
                    }
                    // Subscription
                    else if (((mySession = NgnSubscriptionSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.TERMINATING);
                        mSipService.broadcastSubscriptionEvent(new NgnSubscriptionEventArgs(
                                        sessionId,
                                        NgnSubscriptionEventTypes.UNSUBSCRIPTION_INPROGRESS,
                                        eventCode, phrase, null, null,
                                        ((NgnSubscriptionSession) mySession).getEventPackage()));
                    }

                    break;
                }

                // == Terminated == //
                case tinyWRAPConstants.tsip_event_code_dialog_terminated: {
                    // Registration
                    if (mSipService.mRegSession != null && mSipService.mRegSession.getId() == sessionId) {
                        mSipService.mRegSession.setConnectionState(ConnectionState.TERMINATED);
                        mSipService.broadcastRegistrationEvent(new NgnRegistrationEventArgs(sessionId, NgnRegistrationEventTypes.UNREGISTRATION_OK, sipCode, phrase));
                    /*
                     * Stop the stack (as we are already in the stack-thread,
					 * then do it in a new thread)
					 */
                        new Thread(() -> {
                            if (mSipService.mSipStack.getState() == STACK_STATE.STARTING || mSipService.mSipStack.getState() == STACK_STATE.STARTED) {
                                mSipService.mSipStack.stop();
                            }
                        }).start();
                    }
                    // PagerMode IM
                    else if (NgnMessagingSession.hasSession(sessionId)) {
                        NgnMessagingSession.releaseSession(sessionId);
                    }
                    // Audio/Video/MSRP(Chat, FileTransfer)
                    else if (((mySession = NgnAVSession.getSession(sessionId)) != null) || ((mySession = NgnMsrpSession.getSession(sessionId)) != null)) {
                        mySession.setConnectionState(ConnectionState.TERMINATED);
                        ((NgnInviteSession) mySession).setState(InviteState.TERMINATED);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(sessionId, NgnInviteEventTypes.TERMINATED, ((NgnInviteSession) mySession).getMediaType(), phrase), sipCode);
                        if (mySession instanceof NgnAVSession) {
                            NgnAVSession.releaseSession((NgnAVSession) mySession);
                        } else if (mySession instanceof NgnMsrpSession) {
                            NgnMsrpSession.releaseSession((NgnMsrpSession) mySession);
                        }
                    }
                    // Publication
                    else if (((mySession = NgnPublicationSession.getSession(sessionId)) != null)) {
                        ConnectionState previousConnState = mySession.getConnectionState();
                        mySession.setConnectionState(ConnectionState.TERMINATED);
                        mSipService
                                .broadcastPublicationEvent(new NgnPublicationEventArgs(
                                        sessionId,
                                        (previousConnState == ConnectionState.TERMINATING) ? NgnPublicationEventTypes.UNPUBLICATION_OK
                                                                                           : NgnPublicationEventTypes.PUBLICATION_NOK,
                                        sipCode, phrase));
                    }
                    // Subscription
                    else if (((mySession = NgnSubscriptionSession.getSession(sessionId)) != null)) {
                        ConnectionState previousConnState = mySession.getConnectionState();

                        mySession.setConnectionState(ConnectionState.TERMINATED);
                        mSipService.broadcastSubscriptionEvent(new NgnSubscriptionEventArgs(
                                        sessionId,
                                        (previousConnState == ConnectionState.TERMINATING) ? NgnSubscriptionEventTypes.UNSUBSCRIPTION_OK
                                                : NgnSubscriptionEventTypes.SUBSCRIPTION_NOK,
                                        sipCode, phrase, null, null,
                                        ((NgnSubscriptionSession) mySession).getEventPackage()));
                    }
                    break;
                }
            }

            return 0;
        }

        @Override
        public int OnInviteEvent(InviteEvent e) {
            final tsip_invite_event_type_t type = e.getType();
            final short code = e.getCode();
            final String phrase = e.getPhrase();
            InviteSession session = e.getSession();
            NgnSipSession mySession = null;

            switch (type) {
                case tsip_i_newcall:
                case tsip_i_ect_newcall:
                    if (session != null) /* As we are not the owner, then the session MUST be null */ {
                        Log.e(TAG, "Invalid incoming session");
                        session.hangup(); // To avoid another callback event
                        return -1;
                    }

                    SipMessage message = e.getSipMessage();
                    if (message == null) {
                        Log.e(TAG, "Invalid message");
                        return -1;
                    }
                    final twrap_media_type_t sessionType = e.getMediaType();
                    if (sessionType == twrap_media_type_t.twrap_media_msrp) {
                        if ((session = e.takeMsrpSessionOwnership()) == null) {
                            Log.e(TAG, "Failed to take MSRP session ownership");
                            return -1;
                        }

                        NgnMsrpSession msrpSession = NgnMsrpSession.takeIncomingSession(mSipService.getSipStack(), (MsrpSession) session, message);
                        if (msrpSession == null) {
                            Log.e(TAG, "Failed to create new session");
                            session.hangup();
                            session.delete();
                            return 0;
                        }
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(msrpSession.getId(), NgnInviteEventTypes.INCOMING, msrpSession.getMediaType(), phrase));
                    } else if ((sessionType == twrap_media_type_t.twrap_media_audio) ||
                            (sessionType == twrap_media_type_t.twrap_media_audio_video) ||
                            (sessionType == twrap_media_type_t.twrap_media_audiovideo) ||
                            (sessionType == twrap_media_type_t.twrap_media_video) ||
                            (sessionType.swigValue() == (twrap_media_type_t.twrap_media_audio.swigValue() | twrap_media_type_t.twrap_media_t140.swigValue())) ||
                            (sessionType.swigValue() == (twrap_media_type_t.twrap_media_audio.swigValue() | twrap_media_type_t.twrap_media_video.swigValue() | twrap_media_type_t.twrap_media_t140.swigValue())) ||
                            (sessionType == twrap_media_type_t.twrap_media_t140)) {
                        if ((session = e.takeCallSessionOwnership()) == null) {
                            Log.e(TAG, "Failed to take audio/video session ownership");
                            return -1;
                        }
                        final NgnInviteEventTypes eType = type == tsip_invite_event_type_t.tsip_i_newcall ? NgnInviteEventTypes.INCOMING : NgnInviteEventTypes.REMOTE_TRANSFER_INPROGESS;
                        final NgnAVSession avSession = NgnAVSession.takeIncomingSession(mSipService.getSipStack(), (CallSession) session, sessionType, message);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(avSession.getId(), eType, avSession.getMediaType(), phrase));
                    } else {
                        Log.e(TAG, "Invalid media type");
                        return 0;
                    }
                    break;

                case tsip_ao_request:
                    // For backward compatibility keep both "RINGING" and "SIP_RESPONSE"
//                    if (phrase.equals("OK")) {
//                        final InviteSession finalSession1 = session;
//                        new Thread(new Runnable() {
//                            @Override
//                            public void run() {
//                                synchronized (NgnSipServiceImpl.talkbackSessions) {
//                                    try {
//                                        NgnSipServiceImpl.talkbackSessions.put((int) finalSession1.getId(), true);
//                                        Log.w("session", "ngn,request-->" + finalSession1.getId());
////                                        Thread.sleep(5 * 1000);
//                                        NgnSipServiceImpl.talkbackSessions.notifyAll();
//                                        Log.w("session", finalSession1.getId() + " notify");
//                                    } catch (Exception e1) {
//                                    }
//                                }
//                            }
//                        }).start();
//
//                    }
                    if (code == 180 && session != null) {
                        if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                            mSipService.broadcastInviteEvent(new NgnInviteEventArgs(mySession.getId(), NgnInviteEventTypes.RINGING, ((NgnInviteSession) mySession).getMediaType(), phrase), code);
                        }
                    }
                    if (session != null) {
                        if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                            mSipService.broadcastInviteEvent(new NgnInviteEventArgs(mySession.getId(), NgnInviteEventTypes.SIP_RESPONSE, ((NgnInviteSession) mySession).getMediaType(), phrase), code);
                        }
                    }
                    break;

                case tsip_i_request: {
                    final SipMessage sipMessage = e.getSipMessage();
                    if (sipMessage != null && session != null && ((mySession = NgnAVSession.getSession(session.getId())) != null)) {
                        if (sipMessage.getRequestType() == tsip_request_type_t.tsip_INFO) {
                            final String contentType = sipMessage.getSipHeaderValue("c");
                            if (NgnStringUtils.equals(contentType, NgnContentType.DOUBANGO_DEVICE_INFO, true)) {
                                final byte content[] = sipMessage.getSipContent();
                                if (content != null) {
                                    final String values[] = new String(content).split("\r\n");
                                    for (String value : values) {
                                        if (value == null) continue;
                                        final String kvp[] = value.split(":");
                                        if (kvp.length == 2) {
                                            if (NgnStringUtils.equals(kvp[0], "orientation", true)) {
                                                if (NgnStringUtils.equals(kvp[1], "landscape", true)) {
                                                    ((NgnInviteSession) mySession).getRemoteDeviceInfo().setOrientation(Orientation.LANDSCAPE);
                                                } else if (NgnStringUtils.equals(kvp[1], "portrait", true)) {
                                                    ((NgnInviteSession) mySession).getRemoteDeviceInfo().setOrientation(Orientation.PORTRAIT);
                                                }
                                            } else if (NgnStringUtils.equals(kvp[0], "lang", true)) {
                                                ((NgnInviteSession) mySession).getRemoteDeviceInfo().setLang(kvp[1]);
                                            }
                                        }
                                    }
                                    mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.REMOTE_DEVICE_INFO_CHANGED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                                }
                            }
                        }
                    }
                    break;
                }
                case tsip_o_ect_trying: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_TRANSFER_TRYING, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_o_ect_accepted: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_TRANSFER_ACCEPTED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_o_ect_completed: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_TRANSFER_COMPLETED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_o_ect_failed: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_TRANSFER_FAILED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_o_ect_notify:
                case tsip_i_ect_notify: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        NgnInviteEventTypes eType = (type == tsip_invite_event_type_t.tsip_o_ect_notify ? NgnInviteEventTypes.LOCAL_TRANSFER_NOTIFY : NgnInviteEventTypes.REMOTE_TRANSFER_NOTIFY);
                        NgnInviteEventArgs args = new NgnInviteEventArgs(session.getId(), eType, ((NgnInviteSession) mySession).getMediaType(), phrase);
                        mSipService.broadcastInviteEvent(args, code);
                    }
                    break;
                }
                case tsip_i_ect_requested: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        final SipMessage sipMessage = e.getSipMessage();
                        if (sipMessage != null) {
                            if (sipMessage.getRequestType() == tsip_request_type_t.tsip_REFER) {
                                String referToUri = sipMessage.getSipHeaderValue("refer-to");
                                if (!NgnStringUtils.isNullOrEmpty(referToUri)) {
                                    NgnInviteEventArgs args = new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.REMOTE_TRANSFER_REQUESTED, ((NgnInviteSession) mySession).getMediaType(), phrase);
                                    mSipService.broadcastTransferRequestEvent(args, referToUri);
                                }
                            }
                        }
                    }

                    break;
                }
                case tsip_i_ect_failed: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.REMOTE_TRANSFER_FAILED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_i_ect_completed: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.REMOTE_TRANSFER_COMPLETED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_early_media: {
                    if (session == null) break;
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        ((NgnInviteSession) mySession).setState(InviteState.EARLY_MEDIA);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.EARLY_MEDIA, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_local_hold_ok: {
//                    if (phrase.equals("OK")) {
//                        NgnSipServiceImpl.talkbackSessions.put((int) session.getId(), false);
//                    }
//                    if (phrase.equals("OK")) {
//                        synchronized (NgnSipServiceImpl.talkbackSessions) {
//                            NgnSipServiceImpl.talkbackSessions.put((int) session.getId(), false);
//                            Log.w("session", "ngn-->hold" + session.getId());
//                        }
//                    }
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        ((NgnInviteSession) mySession).setLocalHold(true);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_HOLD_OK, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_updating: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.MEDIA_UPDATING, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_updated: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        if (mySession instanceof NgnAVSession) {
                            NgnAVSession.handleMediaUpdate(mySession.getId(), e.getMediaType());
                        }
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.MEDIA_UPDATED, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_local_hold_nok: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_HOLD_NOK, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_local_resume_ok: {
//                    if (phrase.equals("OK")) {
//                        final InviteSession finalSession2 = session;
//                        new Thread(new Runnable() {
//                            @Override
//                            public void run() {
//                                synchronized (NgnSipServiceImpl.talkbackSessions) {
//                                    try {
//                                        NgnSipServiceImpl.talkbackSessions.put((int) finalSession2.getId(), true);
//                                        Log.w("session", "ngn-->resume" + finalSession2.getId());
//                                        Thread.sleep(5 * 200);
//                                        NgnSipServiceImpl.talkbackSessions.notifyAll();
//                                        Log.w("session", finalSession2.getId() + " notify");
//                                    } catch (Exception e1) {
//                                    }
//                                }
//                            }
//                        }).start();
//
//                    }
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        ((NgnInviteSession) mySession).setLocalHold(false);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_RESUME_OK, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_local_resume_nok: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.LOCAL_RESUME_NOK, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_remote_hold: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        ((NgnInviteSession) mySession).setRemoteHold(true);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.REMOTE_HOLD, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                case tsip_m_remote_resume: {
                    if (((mySession = NgnAVSession.getSession(session.getId())) != null) || ((mySession = NgnMsrpSession.getSession(session.getId())) != null)) {
                        ((NgnInviteSession) mySession).setRemoteHold(false);
                        mSipService.broadcastInviteEvent(new NgnInviteEventArgs(session.getId(), NgnInviteEventTypes.REMOTE_RESUME, ((NgnInviteSession) mySession).getMediaType(), phrase));
                    }
                    break;
                }
                default: {
                    break;
                }
            }

            return 0;
        }

        @Override
        public int OnMessagingEvent(MessagingEvent e) {
            final tsip_message_event_type_t type = e.getType();
            MessagingSession _session;
            final SipMessage message;

            switch (type) {
                case tsip_ao_message:
                    _session = e.getSession();
                    message = e.getSipMessage();
                    short code = e.getCode();
                    if (_session != null && code >= 200 && message != null) {
                        mSipService
                                .broadcastMessagingEvent(
                                        new NgnMessagingEventArgs(
                                                _session.getId(),
                                                (code >= 200 && code <= 299) ? NgnMessagingEventTypes.SUCCESS
                                                        : NgnMessagingEventTypes.FAILURE,
                                                e.getPhrase(), new byte[0], null),
                                        message.getSipHeaderValue("f"),
                                        NgnDateTimeUtils.now());
                    }
                    break;
                case tsip_i_message:
                    message = e.getSipMessage();
                    _session = e.getSession();
                    NgnMessagingSession imSession;
                    if (_session == null) {
                    /*
                     * "Server-side-session" e.g. Initial MESSAGE sent by the
					 * remote party
					 */
                        _session = e.takeSessionOwnership();
                    }

                    if (_session == null) {
                        Log.e(NgnSipService.TAG, "Failed to take session ownership");
                        return -1;
                    }
                    imSession = NgnMessagingSession.takeIncomingSession(mSipService.mSipStack, _session, message);
                    if (message == null) {
                        imSession.reject();
                        imSession.decRef();
                        return 0;
                    }

                    String from = message.getSipHeaderValue("f");
                    final String contentType = message.getSipHeaderValue("c");
                    final byte[] bytes = message.getSipContent();
                    byte[] content = null;

                    if (bytes == null || bytes.length == 0) {
                        Log.e(NgnSipService.TAG, "Invalid MESSAGE");
                        imSession.reject();
                        imSession.decRef();
                        return 0;
                    }

                    imSession.accept();

                    if (NgnStringUtils.equals(contentType, NgnContentType.SMS_3GPP,
                            true)) {
                    /* ==== 3GPP SMSIP === */
                        ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
                        buffer.put(bytes);
                        SMSData smsData = SMSEncoder.decode(buffer,
                                buffer.capacity(), false);
                        if (smsData != null) {
                            twrap_sms_type_t smsType = smsData.getType();
                            if (smsType == twrap_sms_type_t.twrap_sms_type_rpdata) {
                            /* === We have received a RP-DATA message === */
                                long payLength = smsData.getPayloadLength();
                                String SMSC = message
                                        .getSipHeaderValue("P-Asserted-Identity");
                                String SMSCPhoneNumber;
                                String origPhoneNumber = smsData.getOA();

							/* Destination address */
                                if (origPhoneNumber != null) {
                                    from = NgnUriUtils
                                            .makeValidSipUri(origPhoneNumber);
                                } else if ((origPhoneNumber = NgnUriUtils
                                        .getValidPhoneNumber(from)) == null) {
                                    Log.e(NgnSipService.TAG,
                                            "Invalid destination address");
                                    return 0;
                                }

							/*
							 * SMS Center 3GPP TS 24.341 - 5.3.2.4 Sending a
							 * delivery report The address of the IP-SM-GW is
							 * received in the P-Asserted-Identity header in the
							 * SIP MESSAGE request including the delivered short
							 * message.
							 */
                                if ((SMSCPhoneNumber = NgnUriUtils
                                        .getValidPhoneNumber(SMSC)) == null) {
                                    SMSC = NgnEngine
                                            .getInstance()
                                            .getConfigurationService()
                                            .getString(
                                                    NgnConfigurationEntry.RCS_SMSC,
                                                    NgnConfigurationEntry.DEFAULT_RCS_SMSC);
                                    if ((SMSCPhoneNumber = NgnUriUtils
                                            .getValidPhoneNumber(SMSC)) == null) {
                                        Log.e(NgnSipService.TAG,
                                                "Invalid IP-SM-GW address");
                                        return 0;
                                    }
                                }

                                if (payLength > 0) {
								/* Send RP-ACK */
                                    RPMessage rpACK = SMSEncoder.encodeACK(
                                            smsData.getMR(), SMSCPhoneNumber,
                                            origPhoneNumber, false);
                                    if (rpACK != null) {
                                        long ack_len = rpACK.getPayloadLength();
                                        if (ack_len > 0) {
                                            buffer = ByteBuffer
                                                    .allocateDirect((int) ack_len);
                                            long len = rpACK.getPayload(buffer,
                                                    buffer.capacity());
                                            MessagingSession m = new MessagingSession(
                                                    mSipService.getSipStack());
                                            m.setToUri(SMSC);
                                            m.addHeader("Content-Type",
                                                    NgnContentType.SMS_3GPP);
                                            m.addHeader(
                                                    "Content-Transfer-Encoding",
                                                    "binary");
                                            //m.addCaps("+g.3gpp.smsip");
                                            m.send(buffer, len);
                                            m.delete();
                                        }
                                        rpACK.delete();
                                    }

								/* Get ascii content */
                                    buffer = ByteBuffer
                                            .allocateDirect((int) payLength);
                                    content = new byte[(int) payLength];
                                    smsData.getPayload(buffer, buffer.capacity());
                                    buffer.get(content);
                                } else {
								/* Send RP-ERROR */
                                    RPMessage rpError = SMSEncoder.encodeError(
                                            smsData.getMR(), SMSCPhoneNumber,
                                            origPhoneNumber, false);
                                    if (rpError != null) {
                                        long err_len = rpError.getPayloadLength();
                                        if (err_len > 0) {
                                            buffer = ByteBuffer
                                                    .allocateDirect((int) err_len);
                                            long len = rpError.getPayload(buffer,
                                                    buffer.capacity());

                                            MessagingSession m = new MessagingSession(
                                                    mSipService.getSipStack());
                                            m.setToUri(SMSC);
                                            m.addHeader("Content-Type",
                                                    NgnContentType.SMS_3GPP);
                                            m.addHeader("Transfer-Encoding",
                                                    "binary");
//                                            m.addCaps("+g.3gpp.smsip");
                                            m.send(buffer, len);
                                            m.delete();
                                        }
                                        rpError.delete();
                                    }
                                }
                            } else {
							/* === We have received any non-RP-DATA message === */
                                if (smsType == twrap_sms_type_t.twrap_sms_type_ack) {
								/*
								 * Find message from the history (by MR) an
								 * update it's status
								 */
                                    Log.d(NgnSipService.TAG, "RP-ACK");
                                } else if (smsType == twrap_sms_type_t.twrap_sms_type_error) {
								/*
								 * Find message from the history (by MR) an
								 * update it's status
								 */
                                    Log.d(NgnSipService.TAG, "RP-ERROR");
                                }
                            }
                        }
                    } else {
					/* ==== text/plain or any other === */
                        content = bytes;
                    }

				/* Alert the user and add the message to the history */
                    if (content != null) {
                        mSipService
                                .broadcastMessagingEvent(
                                        new NgnMessagingEventArgs(_session.getId(),
                                                NgnMessagingEventTypes.INCOMING, e
                                                .getPhrase(), content,
                                                contentType), from,
                                        NgnDateTimeUtils.now());
                    }

                    break;
            }

            return 0;
        }

        @Override
        public int OnStackEvent(StackEvent e) {
            // final String phrase = e.getPhrase();
            final short code = e.getCode();
            switch (code) {
                case tinyWRAPConstants.tsip_event_code_stack_started:
                    mSipService.mSipStack.setState(STACK_STATE.STARTED);
//                    Log.d(NgnSipService.TAG, "Stack started");
                    break;
                case tinyWRAPConstants.tsip_event_code_stack_failed_to_start:
                    final String phrase = e.getPhrase();
                    Log.e(TAG, String.format(
                            "Failed to start the stack. \nAdditional info:\n%s",
                            phrase));
                    break;
                case tinyWRAPConstants.tsip_event_code_stack_failed_to_stop:
                    Log.e(TAG, "Failed to stop the stack");
                    break;
                case tinyWRAPConstants.tsip_event_code_stack_stopped:
                    mSipService.mSipStack.setState(STACK_STATE.STOPPED);
                    if (mSipService.mTerminateRequested) {
                        mSipService.mActiveConnection = null;
                    }
                    mSipService.mNetworkService.bindProcessToConnection(null);
                    Log.d(TAG, "Stack stopped");
                    break;
                case tinyWRAPConstants.tsip_event_code_stack_disconnected:
                    mSipService.mSipStack.setState(STACK_STATE.DISCONNECTED);
                    if (mSipService.mTerminateRequested) {
                        mSipService.mActiveConnection = null;
                    }
                    Log.d(TAG, "Stack disconnected");
                    break;
            }
            return 0;
        }

        @Override
        public int OnSubscriptionEvent(SubscriptionEvent e) {
            final tsip_subscribe_event_type_t type = e.getType();
            SubscriptionSession _session = e.getSession();

            switch (type) {
                case tsip_i_notify: {
                    final short code = e.getCode();
                    final String phrase = e.getPhrase();
                    final SipMessage message = e.getSipMessage();
                    if (message == null || _session == null) {
                        return 0;
                    }
                    final String contentType = message.getSipHeaderValue("c");
                    final byte[] content = message.getSipContent();

                    if (NgnStringUtils.equals(contentType, NgnContentType.REG_INFO,
                            true)) {
                        // mReginfo = content;
                    } else if (NgnStringUtils.equals(contentType,
                            NgnContentType.WATCHER_INFO, true)) {
                        // mWInfo = content;
                    }

                    NgnSubscriptionSession ngnSession = NgnSubscriptionSession
                            .getSession(_session.getId());
                    NgnSubscriptionEventArgs eargs = new NgnSubscriptionEventArgs(
                            _session.getId(),
                            NgnSubscriptionEventTypes.INCOMING_NOTIFY, code,
                            phrase, content, contentType,
                            ngnSession == null ? EventPackageType.None : ngnSession
                                    .getEventPackage());
                    mSipService.broadcastSubscriptionEvent(eargs);

                    break;
                }

                case tsip_ao_notify:
                case tsip_i_subscribe:
                case tsip_ao_subscribe:
                case tsip_i_unsubscribe:
                case tsip_ao_unsubscribe:
                default: {
                    break;
                }
            }

            return 0;
        }

        @Override
        public int OnOptionsEvent(OptionsEvent e) {
            final tsip_options_event_type_t type = e.getType();
            OptionsSession ptSession = e.getSession();

            switch (type) {
                case tsip_i_options:
                    if (ptSession == null) { // New session
                        if ((ptSession = e.takeSessionOwnership()) != null) {
                            ptSession.accept();
                            ptSession.delete();
                        }
                    }
                    break;
                default:
                    break;
            }
            return 0;
        }
    }
}
